package importExport;

import java.awt.Color;

import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.io.File;
import java.util.Vector;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;


public class KMLExporter{
// units meters & decimaldegrees;
AffineTransform at=new AffineTransform();
double lon;// x laenge westen
double lat;// y breite norden
static double meterPerDegreeLat=110952;
double meterPerDegreeLong;

static double aR=6378137;
static double bR=6356752.3;

Document kmlDoc;
Element root;
Element document;
Element currentFolder;
Element currentPlacemark;
Element currentMultGeometry;

public static String RELATIVETOGROUND = "relativeToGround";
public static String RELATIVETOSEAFLOOR = "relativeToSeaFloor";
public static String ABSOLUTE = "absolute";
public static String CLAMPTOGROUND = "clampToGround";
public KMLExporter(double longitude,double latitude){
	// in degrees
	
	this.lat=latitude;
	this.lon=longitude;
	kmlDoc= createXMLDoc();
	root=kmlDoc.getDocumentElement();
	 
	document= kmlDoc.createElement("Document");
	root.appendChild(document);
	
	
	double cos=Math.cos(Math.toRadians(lat));
	double sin=Math.sin(Math.toRadians(lat));
	meterPerDegreeLong=(Math.PI/180.)*cos*Math.sqrt((aR*aR*aR*aR*cos*cos + bR*bR*bR*bR*sin*sin)/((aR*cos)*(aR*cos) + (bR*sin)*(bR*sin)));
}

public void newFolder(String name){
	currentFolder=kmlDoc.createElement("Folder");
	this.appendName(currentFolder, name);
	document.appendChild(currentFolder);
}
public void newFolder(String name,String start,String end){
	currentFolder=kmlDoc.createElement("Folder");
	this.appendName(currentFolder, name);
	this.appendTimeSpan(currentFolder, start, end);
	document.appendChild(currentFolder);
}

public void newPlacemark(String name){
	currentPlacemark=kmlDoc.createElement("Placemark");
	currentMultGeometry=kmlDoc.createElement("MultiGeometry");
	currentPlacemark.appendChild(currentMultGeometry);
	this.appendName(currentPlacemark, name);
	if (currentFolder==null){
		this.newFolder("");
	}
	currentFolder.appendChild(currentPlacemark);
}

	public void newPlacemark(String name, Color fillColor, Color strokeColor,
			float strokeWidth) {
		newPlacemark(name);
		Element style = kmlDoc.createElement("Style");
		boolean isStroke = true;
		boolean isFill = true;
		if (strokeColor == null)
			isStroke = false;
		if (fillColor == null)
			isFill = false;
		this.appendPolyStyle(style, fillColor, isFill, isStroke);
		if (isStroke) {
			this.appendLineStyle(style, strokeColor, strokeWidth);
		}
		currentPlacemark.appendChild(style);
	}

public void newPlacemark(String name,Color fillColor,Color strokeColor,float strokeWidth,String tStart,String tEnd){
	newPlacemark(name, fillColor, strokeColor,strokeWidth);
	this.appendTimeSpan(currentPlacemark, tStart, tEnd);
}
public void drawPoint2D(Point2D p){
	if (currentPlacemark==null){
		this.newPlacemark("");
	}
	appendPoint(currentPlacemark,p);
}
public void drawExtrudeLine(Point2D p1,Point2D p2,double bottom,double top,String altitudeMode){
	if (currentPlacemark==null){
		this.newPlacemark("");
	}
	appendPolygon(currentMultGeometry,p1,p2,bottom,top,altitudeMode);
}

public void drawExtrudeShape(Vector<Point2D> points,double bottom,double top,String altitudeMode){
	if (currentPlacemark==null){
		this.newPlacemark("");
	}
	 appendExtrudeShape(currentMultGeometry,points,bottom,top,altitudeMode);
}



public Element drawExtrudeShapeToGround(Vector<Point2D> points,double top,String altitudeMode){
	if (currentPlacemark==null){
		this.newPlacemark("");
	}
	Vector<Point2D>pointsClosed=new Vector<Point2D>(points);
	pointsClosed.add(pointsClosed.firstElement());
	return appendPolygon(currentMultGeometry,pointsClosed,top,true,false,altitudeMode);
}
public Element drawLineString(Vector<Point2D> points,double z,boolean isExtrude,boolean isTesselate,String altitudeMode){
	if (currentPlacemark==null){
		this.newPlacemark("");
	}
	return this.appendLineString(currentMultGeometry, points, z, isExtrude,isTesselate,altitudeMode);	
}

public void writeXmlFile(File file) {
	if (file != null) {
		try {
			// Prepare the DOM document for writing
			Source source = new DOMSource(kmlDoc);

			// Prepare the output file

			Result result = new StreamResult(file);

			// Write the DOM document to the file
			Transformer xformer = TransformerFactory.newInstance()
					.newTransformer();
			xformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,
					"yes");
			xformer.setOutputProperty(OutputKeys.INDENT, "yes");
			xformer.transform(source, result);
		
		} catch (TransformerConfigurationException e) {
		} catch (TransformerException e) {
		}
	}
	System.out.println("export done.");
}

private Document createXMLDoc() {
	Document cDoc = null;
	DocumentBuilderFactory docBFac;
	DocumentBuilder docBuild;
	
	try {
		docBFac = DocumentBuilderFactory.newInstance();
		docBuild = docBFac.newDocumentBuilder();
		cDoc=docBuild.getDOMImplementation().createDocument("http://www.opengis.net/kml/2.2","kml", null);
		
		//cDoc = docBuild.newDocument();
	} catch (Exception e) {
		System.out.println("Dokument konnte nicht erzeugt werden");
	}
	//cDoc.createAttributeNS("http://www.w3.org/2000/xmlns/", "kml");
	return cDoc;
}

private Element appendPolygon(Element parent,Vector<Point2D> points,double z,boolean isExtrude,boolean isTesselate,String altitudeMode){
	
	String crds="";
	for (int i=0;i<points.size();i++){
		Point2D p=points.get(i);
		crds+=this.getLon(p.getX())+","+this.getLat(p.getY())+","+z+" ";
	}
	Element coordinates=parent.getOwnerDocument().createElement("coordinates");
	coordinates.setTextContent(crds);
	
	Element linearRing=parent.getOwnerDocument().createElement("LinearRing");
	linearRing.appendChild(coordinates);
	
	Element outerBoundaryIs=parent.getOwnerDocument().createElement("outerBoundaryIs");
	outerBoundaryIs.appendChild(linearRing);
	
	Element polygon=parent.getOwnerDocument().createElement("Polygon");
	polygon.appendChild(outerBoundaryIs);
	
	this.appendAltitudeMode(polygon, altitudeMode);
	this.appendTesselate(polygon, isTesselate);
	this.appendExtrude(polygon, isExtrude);
	parent.appendChild(polygon);
	return polygon;
}

private Element appendPoint(Element parent,Point2D p){
	
	double x=this.getLon(p.getX());
	double y=this.getLat(p.getY());
	String crds=x+","+y;
	Element coordinates=parent.getOwnerDocument().createElement("coordinates");
	coordinates.setTextContent(crds);
	
	Element eLPoint=parent.getOwnerDocument().createElement("Point");
	eLPoint.appendChild(coordinates);
	
	
	parent.appendChild(eLPoint);
	return eLPoint;
}

private Element appendPolygon(Element parent,Point2D p1,Point2D p2,double z1,double z2,String altitudeMode){
	
	double x1=this.getLon(p1.getX());
	double y1=this.getLat(p1.getY());
	double x2=this.getLon(p2.getX());
	double y2=this.getLat(p2.getY());
	String crds=x1+","+y1+","+z1+" "+x2+","+y2+","+z1+" "+x2+","+y2+","+z2+" "+x1+","+y1+","+z2;
	Element coordinates=parent.getOwnerDocument().createElement("coordinates");
	coordinates.setTextContent(crds);
	
	Element linearRing=parent.getOwnerDocument().createElement("LinearRing");
	linearRing.appendChild(coordinates);
	
	Element outerBoundaryIs=parent.getOwnerDocument().createElement("outerBoundaryIs");
	outerBoundaryIs.appendChild(linearRing);
	
	Element polygon=parent.getOwnerDocument().createElement("Polygon");
	polygon.appendChild(outerBoundaryIs);
	
	this.appendAltitudeMode(polygon, altitudeMode);
	
	parent.appendChild(polygon);
	return polygon;
}

private Element appendLineString(Element parent,Vector<Point2D> points,double z,boolean isExtrude,boolean isTesselate,String altitudeMode){
	String crds="";
	for (int i=0;i<points.size();i++){
		Point2D p=points.get(i);
		crds+=this.getLon(p.getX())+","+this.getLat(p.getY())+","+z+" ";
	}
	Element coordinates=parent.getOwnerDocument().createElement("coordinates");
	coordinates.setTextContent(crds);
	Element lineString=parent.getOwnerDocument().createElement("LineString");
	lineString.appendChild(coordinates);
	this.appendAltitudeMode(lineString, altitudeMode);
	this.appendExtrude(lineString, isExtrude);
	parent.appendChild(lineString);
	return lineString;
}

private Element appendAltitudeMode(Element parent,String mode){
	Element altitude=parent.getOwnerDocument().createElement("altitudeMode");
	altitude.setTextContent(mode);
	parent.appendChild(altitude);
	return altitude;
}

private void appendExtrudeShape(Element parent, Vector<Point2D> points,
		double bottom, double top, String altitudeMode) {

	this.appendPolygon(parent, points, bottom, false, false, altitudeMode);
	this.appendPolygon(parent, points, top, false, false, altitudeMode);

	for (int i = 0; i < points.size(); i++) {
		int iNext = i + 1;
		if (iNext == points.size())
			iNext = 0;
		Point2D p1 = points.get(i);
		Point2D p2 = points.get(iNext);
		this.appendPolygon(parent, p1, p2, bottom, top, altitudeMode);
	}

}

private Element appendTimeSpan(Element parent, String start, String end) {
	Element timeSpan = parent.getOwnerDocument().createElement("TimeSpan");
	Element beginEl = parent.getOwnerDocument().createElement("begin");
	beginEl.setTextContent(start);
	Element endEl = parent.getOwnerDocument().createElement("end");
	endEl.setTextContent(end);
	timeSpan.appendChild(beginEl);
	timeSpan.appendChild(endEl);

	parent.appendChild(timeSpan);
	return timeSpan;
}

private Element appendTesselate(Element parent,boolean isTesselate){
	Element tesselate=parent.getOwnerDocument().createElement("tesselate");
	if (isTesselate)
		tesselate.setTextContent("1");
	else tesselate.setTextContent("0");
	parent.appendChild(tesselate);
	return tesselate;
}

private Element appendExtrude(Element parent,boolean isExtrude){
	Element extrude=parent.getOwnerDocument().createElement("extrude");
	if (isExtrude)
	extrude.setTextContent("1");
	else extrude.setTextContent("0");
	parent.appendChild(extrude);
	return extrude;
}

private Element appendName(Element parent,String name){
	Element nameEl=parent.getOwnerDocument().createElement("name");
	nameEl.setTextContent(name);
	parent.appendChild(nameEl);
	return nameEl;
}

private Element appendColor(Element parent,Color c){
	Element color=parent.getOwnerDocument().createElement("color");
	color.setTextContent(this.getHexColor(c));
	parent.appendChild(color);
	return color;
}

private Element appendLineStyle(Element parent,Color c,float w){
	
	Element lineStyle=parent.getOwnerDocument().createElement("LineStyle");
	Element width=parent.getOwnerDocument().createElement("width");
	width.setTextContent(""+w);
	lineStyle.appendChild(width);
	this.appendColor(lineStyle, c);
	
	parent.appendChild(lineStyle);
	
	return lineStyle;
	
}

private Element appendPolyStyle(Element parent,Color c,boolean isFill,boolean isOutline){
	Element polyStyle=parent.getOwnerDocument().createElement("PolyStyle");
	Element outline=parent.getOwnerDocument().createElement("outline");
	Element fill=parent.getOwnerDocument().createElement("fill");
	
	if (isFill)fill.setTextContent("1");
	else fill.setTextContent("0");
	if (isOutline)outline.setTextContent("1");
	else outline.setTextContent("0");
	
	polyStyle.appendChild(fill);
	polyStyle.appendChild(outline);
	
	this.appendColor(polyStyle, c);
	
	parent.appendChild(polyStyle);
	return polyStyle;
}

private String getHexColor(Color c){
	String color;
	int a=c.getAlpha();
	int r=c.getRed();
	int g=c.getGreen();
	int b=c.getBlue();
	
	color=String.format("%02x",a)+String.format("%02x",b)+String.format("%02x",g)+String.format("%02x",r);

	return color;
}

private double getLat(double y){
	//at.
	return y/meterPerDegreeLat+lat;
}

private double getLon(double x){
	return x/meterPerDegreeLong+lon;
}

public Element getCurrentFolder() {
	return currentFolder;
}

public void setCurrentFolder(Element currentFolder) {
	this.currentFolder = currentFolder;
}

public Element getCurrentMultGeometry() {
	return currentMultGeometry;
}

public void setCurrentMultGeometry(Element currentMultGeometry) {
	this.currentMultGeometry = currentMultGeometry;
}

public Element getCurrentPlacemark() {
	return currentPlacemark;
}

public void setCurrentPlacemark(Element currentPlacemark) {
	this.currentPlacemark = currentPlacemark;
}

public Element getDocument() {
	return document;
}

public void setDocument(Element document) {
	this.document = document;
}

public Document getKmlDoc() {
	return kmlDoc;
}

public void setKmlDoc(Document kmlDoc) {
	this.kmlDoc = kmlDoc;
}

public Element getRoot() {
	return root;
}

public void setRoot(Element root) {
	this.root = root;
}


}
